﻿using Circus.Utility;
using Circus.Utility.Json;
using System;
using System.Data;
using System.Drawing;
using System.IO;
using System.Web;

namespace Teal.Club {

    /// <summary>
    /// 提供文件控制相关接口。
    /// </summary>
    public static class FileController {

        private const string imageUploadPath = "~/file/images/";
        private const string imageUploadUrl = "/file/images/";

        /// <summary>
        /// 上传一个图片。
        /// </summary>
        [post]
        public static void uploadImage() {
            upload(imageUploadPath, imageUploadUrl, 10 * 1024 * 1024, FileType.image);
        }

        private const string faceUploadPath = "~/upload/faces/";
        private const string faceUploadUrl = "/upload/faces/";

        /// <summary>
        /// 上传一个头像。
        /// </summary>
        internal static void uploadFace() {
            upload(faceUploadPath, faceUploadUrl, 2 * 1024 * 1024, FileType.face);
        }

        /// <summary>
        /// 删除指定的图片。
        /// </summary>
        /// <param name="path">要删除的文件。</param>
        /// <returns></returns>
        public static string deleteImage(string path) {
            File.Delete(HttpContext.Current.Request.MapPath(imageUploadPath) + path);
            return String.Empty;
        }

        private const string fileUploadPath = "~/download/userfiles/";
        private const string fileUploadUrl = "/download/";

        /// <summary>
        /// 上传一个文件。
        /// </summary>
        [post]
        public static void uploadFile() {
            upload(fileUploadPath, fileUploadUrl, 20 * 1024 * 1024, FileType.userFile);
        }

        /// <summary>
        /// 删除指定的文件。
        /// </summary>
        /// <param name="path">要删除的文件。</param>
        /// <returns></returns>
        public static string deleteFile(string path) {
            var currentUser = UserController.currentUser;
            using (var conn = DbHelper.createConnection()) {
                var cmd = conn.createProcedure("file_delete");
                cmd.Parameters.AddWithValue("@filePath", path);
                cmd.Parameters.AddWithValue("@uploadUserId", currentUser.id);
                if (cmd.ExecuteNonQuery() == 0) {
                    return "{\"ret\":1,\"msg\":\"删除失败\"}";
                }

                File.Delete(HttpContext.Current.Request.MapPath(fileUploadPath) + path);
                return CommonResponse.success;
            }
        }

        #region 内部实现

        private enum FileType {
            image,
            flash,
            face,
            userFile,
        }

        private static void upload(string basePath, string baseUrl, int maxUploadBytes, FileType fileType) {

            // 确认用户登录。
            string userName = UserController.currentUser.name;

            // 确认用户已上传文件。
            var context = HttpContext.Current;
            if (context.Request.Files.Count == 0) {
                context.Response.Write("{\"code\":-1,\"msg\":\"没有上传文件\"}");
                return;
            }

            // 确认文件是否超限。
            HttpPostedFile file = context.Request.Files[0];
            if (file.ContentLength > maxUploadBytes) {
                context.Response.Write("{\"code\":1,\"msg\":\"文件上传失败：最大只能上传 " + byteName(maxUploadBytes) + "，现在是 " + byteName(file.ContentLength) + "\"}");
                return;
            }

            // 转换基路径。
            string fileName = file.FileName;
            string ext = Path.GetExtension(fileName);
            string filePath;
            basePath = context.Request.MapPath(basePath);

            // 确认文件格式是否符合要求。
            switch (fileType) {
                case FileType.image:
                    switch (ext) {
                        case ".jpg":
                        case ".png":
                        case ".bmp":
                        case ".gif":
                        case ".ico":
                            break;
                        default:
                            context.Response.Write("{\"ret\":2,\"msg\":\"文件上传失败：只能上传 .jpg、.png、.bmp、.gif 或 .ico 格式的图片\"}");
                            return;
                    }
                    goto default;
                case FileType.flash:
                    if (ext != ".swf") {
                        context.Response.Write("{\"ret\":2,\"msg\":\"文件上传失败：只能上传 .swf 格式的 Flash 动画\"}");
                        return;
                    }
                    goto default;
                case FileType.face:
                    // 替换不允许字符。
                    foreach (var ch in Path.GetInvalidFileNameChars()) {
                        userName = userName.Replace(ch, '_');
                    }
                    fileName = userName + ext;
                    filePath = basePath + fileName;
                    break;
                default:
                    // 替换不允许字符。
                    foreach (var ch in Path.GetInvalidPathChars()) {
                        userName = userName.Replace(ch, '_');
                    }
                    fileName = userName + '/' + fileName;
                    filePath = basePath + fileName;

                    // 确认文件不存在。
                    if (File.Exists(filePath)) {
                        string orignalFileName = fileName.Substring(0, fileName.Length - ext.Length);
                        int id = 0;
                        do {
                            fileName = orignalFileName + (++id) + ext;
                            filePath = basePath + fileName;
                        } while (File.Exists(filePath));
                    } else {
                        basePath += '/' + userName;
                    }

                    if (fileType == FileType.userFile) {
                        using (var conn = DbHelper.createConnection()) {
                            var cmd = conn.createProcedure("file_upload");
                            cmd.Parameters.AddWithValue("@filePath", fileName);
                            cmd.Parameters.AddWithValue("@uploadUserId", UserController.currentUser.id);
                            cmd.ExecuteNonQuery();
                        }
                    }
                    break;

            }

            Directory.CreateDirectory(basePath);

            if (fileType == FileType.image) {
                // 添加水印。
                Image img = Image.FromStream(file.InputStream);
                img.Save(filePath);
            } else {
                file.SaveAs(filePath);
            }

            context.Response.Write("{\"ret\":0,\"url\":");
            context.Response.Write(Json.stringify(baseUrl + fileName));
            context.Response.Write("}");

        }

        public static string byteName(int value) {
            if (value < 1024) {
                return value.ToString("0B");
            }

            if (value < 1024 * 1024) {
                return ((double)value / (1024)).ToString("0.##KB");
            }

            return ((double)value / (1024 * 1024)).ToString("0.##MB");

        }

        private const int downloadBufferSize = 10 * 1024;

        /// <summary>
        /// 下载指定的文件。
        /// </summary>
        internal static void downloadFile(string path) {

            HttpContext context = HttpContext.Current;

            // 停用缓存。
            context.Response.BufferOutput = false;

            // 服务器端的位置。
            string serverPath = context.Request.MapPath(fileUploadPath + path);
            FileStream s;

            try {
                s = File.OpenRead(serverPath);
            } catch (IOException) {
                context.Response.StatusCode = 404;
                return;
            }

            try {

                string range = context.Request.Headers["Range"];
                context.Response.ContentType = "application/octet-stream";
                context.Response.AddHeader("Accept-Ranges", "bytes");
                context.Response.AddHeader("Content-Disposition", "attachment; filename=" + (context.Request.UserAgent.Contains("MSIE") ? HttpUtility.UrlEncode(path) : path));

                // 打开文件流。
                long fileSize = s.Length;
                long contentLength = fileSize;

                if (range == null) {

                    // 统计次数。
                    using (var conn = DbHelper.createConnection()) {
                        var cmd = conn.createProcedure("file_download");
                        cmd.Parameters.AddWithValue("@filePath", path);
                        cmd.ExecuteNonQuery();
                    }

                    context.Response.AddHeader("Content-Length", contentLength.ToString());
                    writeStream(s, context, 0, contentLength);
                } else {
                    long left = 0, right = contentLength - 1;

                    // Range
                    // bytes=500-600,601-999
                    context.Response.StatusCode = 206;

                    // 删除空格
                    range = range.Replace(" ", "");

                    // 找到=
                    int rangeLeftPos = range.IndexOf('=') + 1;

                    // 读取 left
                    for (; rangeLeftPos < range.Length; rangeLeftPos++) {
                        int c = range[rangeLeftPos] - '0';
                        if (c >= 0 && c <= 9) {
                            left = left * 10 + c;
                        } else {
                            break;
                        }
                    }

                    // 测试是 1-4 还是 1
                    if (rangeLeftPos < range.Length - 1 && range[rangeLeftPos] == '-') {
                        bool rightOnly = rangeLeftPos == 0;

                        // 跳过 -
                        rangeLeftPos++;

                        // 读取 right
                        right = 0;

                        // 读取 right
                        for (; rangeLeftPos < range.Length; rangeLeftPos++) {
                            int c = range[rangeLeftPos] - '0';
                            if (c >= 0 && c <= 9) {
                                right = right * 10 + c;
                            } else {
                                break;
                            }
                        }

                        if (rightOnly) {
                            left = contentLength - right;
                            right = contentLength;
                        } else if (right < left) {
                            right = contentLength - 1;
                        }
                    }

                    s.Position = left;

                    long length = right - left;

                    context.Response.AddHeader("Content-Range", String.Concat("bytes ", left.ToString(), "-", right.ToString(), "/" + contentLength));
                    context.Response.AddHeader("Content-Length", length.ToString());

                    writeStream(s, context, left, length);
                }

            } finally {
                s.Close();
            }


        }

        private static bool writeStream(FileStream s, HttpContext context, long start, long length) {
            long bytesLeft = length;
            int maxLength = (int)Math.Min(bytesLeft, downloadBufferSize);
            byte[] buffer = new byte[maxLength];
            while (bytesLeft > 0) {
                if (context.Response.IsClientConnected) {
                    int bytesRead = s.Read(buffer, 0, maxLength);
                    context.Response.OutputStream.Write(buffer, 0, bytesRead);
                    bytesLeft -= bytesRead;
                } else {
                    break;
                }
            }
            return bytesLeft == 0;
        }

        #endregion

    }

}
